package controller.transform;

import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;

import javax.media.j3d.Transform3D;
import javax.vecmath.AxisAngle4d;
import javax.vecmath.Point3d;
import javax.vecmath.Vector3d;

import math.Transforms;

import component.AbstractComponent;
import component.Status;
import component.Tube;
import component.group.ComponentGroup;
import component.group.FindAxis;
import component.traverse.Traverse;

public abstract class AlignMode extends TransformMode {
	
	protected ComponentGroup pivotGroup;
	
	private Tube clicked = null;
	
	protected final boolean isAxis;
	
	public AlignMode(Gui gui, Scene scene, AbstractComponent pivotOrAxis) {
		super(gui, scene);
		
		isAxis = (pivotOrAxis instanceof Tube);
		
		if (isAxis) {
			pivotGroup = new FindAxis().find((Tube)pivotOrAxis);
		} else {
			pivotGroup = new ComponentGroup();
			pivotGroup.add(pivotOrAxis);
		}
		
		pivotGroup.setStatusFlag(Status.HIGHLIGHTED, true);
	}

	@Override
	public TransformMode mousePressed(MouseEvent e) {
		if (e.getButton() == MouseEvent.BUTTON3) {
			this.deselected();
			return new InitMode(gui, scene);
		}
		
		AbstractComponent component = scene.pickClosest(e);
		if (component != null && !pivotGroup.contains(component)) {
			if (component instanceof Tube) {
				clicked = (Tube)component;
				tubeClicked(clicked);
				clicked.setStatus(Status.HIGHLIGHTED, true);
			}
		}
		return this;
	}
	
	abstract protected void tubeClicked(Tube tube);

	@Override
	public TransformMode mouseReleased(MouseEvent e) {
		setDefaultText();
		if (clicked != null) {
			clicked.setStatus(Status.HIGHLIGHTED, false);
			clicked = null;
		}
		return this;
	}

	@Override
	public TransformMode mouseMoved(MouseEvent e) {
		return this;
	}

	@Override
	public TransformMode mouseDragged(MouseEvent e) {
		return this;
	}

	@Override
	public TransformMode keyPressed(KeyEvent e) {
		return this;
	}

	@Override
	public TransformMode keyReleased(KeyEvent e) {
		return this;
	}

	@Override
	public void deselected() {
		pivotGroup.setStatus(Status.NORMAL);
		if (clicked != null) {
			clicked.setStatus(Status.NORMAL);
			clicked = null;
		}
	}
	
	abstract protected void setDefaultText();

	
	protected ComponentGroup makeSelection(AbstractComponent clicked) {
		ComponentGroup selection = new ComponentGroup();
		Traverse traverse = new Traverse();
		traverse.setBorder(pivotGroup);
		traverse.addListener(selection);
		traverse.traverse(clicked, ComponentGroup.SELECT);
		if (selection.containsLockedComponents()) {
			gui.setStatusText("Can't transform, because this part of the structure contains locked components.");
			return null;
		}
		return selection;
	}
	
	protected void alignGroup(ComponentGroup group, AxisAngle4d axisAngle, Point3d pos) {
		Transform3D transform = new Transform3D();
		transform.set(axisAngle);
		transform = Transforms.makeTransformAboutPoint(pos, transform);
		group.applyTransform(transform);
		if (!group.isValidPosition()) {
			transform.invert();
			group.applyTransform(transform);
			gui.setStatusText("Something is blocking this transform.");
		} else {
			scene.connectPossible();
		}
	}
	
	protected AxisAngle4d getTotalRotation(Vector3d from, Vector3d to) {
		Vector3d cross = new Vector3d();
		cross.cross(from, to);
		return new AxisAngle4d(cross, from.angle(to));
	}
	
	protected AxisAngle4d getClosestRotation(Vector3d tubeVector, Vector3d alignTo) {
		Vector3d alignToNegative = new Vector3d(alignTo);
		alignToNegative.negate();
		
		double angle1 = tubeVector.angle(alignTo);
		double angle2 = tubeVector.angle(alignToNegative);
		AxisAngle4d axisAngle;
		if (angle1 < angle2) {
			tubeVector.cross(tubeVector, alignTo);
			axisAngle = new AxisAngle4d(tubeVector, angle1);
		} else {
			tubeVector.cross(tubeVector, alignToNegative);
			axisAngle = new AxisAngle4d(tubeVector, angle2);
		}
		return axisAngle;
	}

}
